package com.tool.app.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.util.ParameterizedTypeImpl;
import com.tool.app.common.CommonPage;
import com.tool.app.common.CommonResult;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class FastJsonUtil {
	private static final ConcurrentMap<Type, Type> classTypeCache = new ConcurrentHashMap<>(16, 0.75f, 1);

	/**
	 * ParameterizedTypeImpl会导致内存泄漏问题
	 * fastJson升级到1.2.83版本或者2.0.23版本解决这个问题
	 * 或者使用以下方法把type保存起来
	 */
	public static Type getType(Type argkey) {
		Type cachedType = classTypeCache.get(argkey);
		if (cachedType == null) {
			classTypeCache.putIfAbsent(argkey, argkey);
		}
		return cachedType == null ? argkey : cachedType;
	}

	/**
	 * 任意对象转json字符串
	 */
	public static String toJSONString(Object obj) {
		return JSON.toJSONString(obj);
	}

	/**
	 * json字符串转JSONObject对象
	 */
	public static JSONObject parseObject(String jsonStr) {
		return JSON.parseObject(jsonStr);
	}

	/**
	 * json字符串转JSONArray对象
	 */
	public static JSONArray parseArray(String jsonListStr) {
		return JSONArray.parseArray(jsonListStr);
	}

	/**
	 * 不推荐使用, 不支持嵌套对象, 会报错
	 * json字符串转JSON对象
	 */
	@Deprecated
	public static Object toJSON(String jsonStr) {
		return JSON.toJSON(jsonStr);
	}

	/**
	 * json字符串转java对象
	 */
	public static <T> T parseObject(String jsonStr, Class<T> objClass) {
		return JSON.parseObject(jsonStr, objClass);
	}

	/**
	 * JSONObject对象转java对象
	 */
	public static <T> T toJavaObject(Object jsonObj, Class<T> objClass) {
		return JSON.toJavaObject((JSON) jsonObj, objClass);
	}

	/**
	 * json字符串转集合
	 */
	public static <T> List<T> parseArray(String jsonListStr, Class<T> clazz) {
		return JSONArray.parseArray(jsonListStr, clazz);
	}

	/**
	 * JSONArray对象转java对象
	 */
	public static <T> List<T> toJavaList(Object jsonArr, Class<T> clazz) {
		return ((JSONArray) jsonArr).toJavaList(clazz);
	}

	/**
	 * 嵌套对象解析
	 */
	public static <T> CommonResult<T> jsonR2bean(String jsonStr, Class<T> clazz) {
		return JSONObject.parseObject(jsonStr, new TypeReference<CommonResult<T>>(clazz) {});
	}

	/**
	 * 嵌套对象集合解析
	 * Type[]  泛型的实际类型列表   泛型类
	 * OwnerType 表示参数化类型的所有者类型，外部类 可以为空
	 * rawType 是不带泛型参数的类型 比如 List，中间类
	 */
	@SuppressWarnings("unchecked")
	public static <T> CommonResult<List<T>> jsonR2List(String jsonStr, Class<T> clazz) {
		return(CommonResult<List<T>>) JSONObject.parseObject(jsonStr,new TypeReference<CommonResult<T>>(new ParameterizedTypeImpl(new Type[]{clazz}, CommonResult.class, ArrayList.class)) {});
	}

	/**
	 * 多层嵌套解析
	 *
	 * @param jsonStr json字符串
	 * @param types   对象class, 由外层到内层
	 */
	public static <T> CommonResult<T> jsonR2NestedBean(String jsonStr, Type... types) {
		return JSONObject.parseObject(jsonStr, buildType(types));
	}

	/**
	 * Map解析
	 * 构造器里的Type列表要与泛型类型参数一一对应
	 * new TypeReference<Table<T1, T2, T3>>(clazz1, clazz2, clazz3){}
	 *
	 * @param jsonStr json字符串
	 * @param keyCls  key类型
	 * @param valCls  value类型
	 */
	public static <K, V> Map<K, V> json2Map(String jsonStr, Class<K> keyCls, Class<V> valCls) {
		return JSONObject.parseObject(jsonStr, new TypeReference<Map<K, V>>(keyCls, valCls) {});
	}


	/**
	 * 为序列化两次以上泛型提供的工具类 Result<List<T>>,作用类似
	 * new TypeReference<Result<List<T>>>(Result.class, List.class, T Clazz) { };
	 */
	public static Type buildType(Type... types) {
		ParameterizedTypeImpl beforeType = null;
		if (types != null && types.length > 0) {
			for (int i = types.length - 1; i > 0; i--) {
				beforeType = new ParameterizedTypeImpl(new Type[]{beforeType == null ? types[i] : beforeType}, null, types[i - 1]);
			}
		}
		return beforeType;
	}

	/**
	 * 嵌套分页对象解析
	 * 分页对象中的泛型必须为List<T> list
	 */
	@SuppressWarnings("unchecked")
	public static <T> CommonResult<CommonPage<T>> jsonR2Page(String jsonStr, Class<T> clazz) {
		return(CommonResult<CommonPage<T>>) JSONObject.parseObject(jsonStr,
				new TypeReference<CommonResult<T>>(new ParameterizedTypeImpl(new Type[]{clazz}, CommonResult.class, CommonPage.class)) {});
	}

	public static void main(String[] args) {

	}

}